#include "lom-class.hh"
#include "lom-errors.hh"
#include "lom-value.hh"

using namespace boost;
using namespace licada;


namespace lom
{
    LomClass::LomClass() { initialize( "", NULL ); }
    LomClass::LomClass( const TNameStr& _name, ILomValueFactory* _factory ) : factory( NULL )
    {
	initialize( _name, _factory );
    }
    
    LomClass::~LomClass() { initialize( "", NULL ); }
    

    LomClass::LomClass( const LomClass& other )
    {
	*this = other;
    }

    static LomValue* _tv( const LomValue* value, ILomValueFactory* factory )
    {
	if ( value != NULL && factory != NULL )
	    return value->clone( factory );
	return NULL;
    }
    
    LomClass& LomClass::operator=( const LomClass& other )
    {
	if ( this != &other )
	{
	    name = other.name;
	    properties = other.properties;
	    factory = other.factory;
	    defaults.resize( other.defaults.size() );
	    std::transform( other.defaults.begin(), other.defaults.end(), defaults.begin(), boost::bind( _tv, _1, factory) );
	}
	return *this;
    }
    
    static void deleteValue( LomValue* val, ILomValueFactory* factory ) 
    { 
	if ( val ) {
	    if ( factory )
		factory->destroy( val ); 
	    else //This means we were initialized incorrectly
		throw LomValueDeletionWithNoFactory();
	}
    }
    
    void LomClass::initialize( const licada::TNameStr& _name, ILomValueFactory* _factory)
    {
	properties.clear();
	std::for_each( defaults.begin(), defaults.end(), boost::bind( deleteValue, _1, factory ) );
	defaults.clear();
	factory = _factory;
	name = _name;
    }
    
    void LomClass::setName( const TNameStr& _name ) {
	name = _name;
    }
    
    void LomClass::addProperty( const LomProperty& prop )
    {
	int32 address = getPropertyIndex( prop.name, false );
	if ( address >= 0 )
	    throw DuplicatePropertyAdded( prop );
	properties.push_back( prop );	
	defaults.push_back( NULL );
    }

    void LomClass::addProperty( const LomProperty& prop, const LomValue& def )
    {
	addProperty( prop );
	if ( factory )
	    defaults.back() = def.clone(factory);
    }
    
    static bool _ne( const LomProperty& prop, const TNameStr& name ) { return prop.name == name; }
    
    int32 LomClass::getPropertyIndex( const TNameStr& name, bool throwFail ) const
    {
	TPropertyVector::const_iterator find = std::find_if( properties.begin(), properties.end(),  bind( _ne, _1, name));
	if ( find == properties.end() )
	{
	    if ( throwFail )
		throw MissingProperty(name);
	    return -1;
	}
	return find - properties.begin();
    }
}
